package com.ruoyi.framework.filter;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.security.ApiEncryptUtils;

/**
 * API加密解密过滤器
 * 统一处理请求解密和响应加密
 * 
 * @author ruoyi
 */
public class ApiEncryptFilter implements Filter {
    
    private static final Logger log = LoggerFactory.getLogger(ApiEncryptFilter.class);
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("ApiEncryptFilter 初始化完成");
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        String requestURI = httpRequest.getRequestURI();
        String method = httpRequest.getMethod();
        String contentType = httpRequest.getContentType();
        
        log.debug("处理请求: {} {}, Content-Type: {}", method, requestURI, contentType);
        
        // 对于GET请求，直接放行，不进行加密解密处理
        if ("GET".equalsIgnoreCase(method)) {
            log.debug("GET请求直接放行: {}", requestURI);
            chain.doFilter(request, response);
            return;
        }
        
        // 只处理POST请求的加密解密
        if (!"POST".equalsIgnoreCase(method)) {
            chain.doFilter(request, response);
            return;
        }
        
        // 检查Content-Type，对于文件上传请求（multipart/form-data）直接放行
        if (contentType != null && contentType.toLowerCase().contains("multipart/form-data")) {
            log.debug("文件上传请求（multipart/form-data）直接放行: {}", requestURI);
            chain.doFilter(request, response);
            return;
        }
        
        try {
            // 读取请求体
            String requestBody = IOUtils.toString(httpRequest.getInputStream(), StandardCharsets.UTF_8);
            
            if (requestBody == null || requestBody.trim().isEmpty()) {
                log.debug("请求体为空，直接放行");
                chain.doFilter(request, response);
                return;
            }
            
            log.debug("原始请求体: {}", requestBody);
            
            // 去除可能的双引号包装
            String cleanedBody = requestBody.trim();
            if (cleanedBody.startsWith("\"") && cleanedBody.endsWith("\"")) {
                cleanedBody = cleanedBody.substring(1, cleanedBody.length() - 1);
                // 处理转义的双引号
                cleanedBody = cleanedBody.replace("\\\"", "\"");
            }
            
            // 解密数据
            String decryptedData;
            try {
                decryptedData = ApiEncryptUtils.decrypt(cleanedBody);
                log.debug("解密成功: {}", decryptedData);
            } catch (Exception e) {
                log.error("解密失败: {}", e.getMessage());
                // 解密失败，尝试直接使用原始数据
                decryptedData = requestBody;
            }
            
            // 创建包装的请求对象
            DecryptedRequestWrapper wrappedRequest = new DecryptedRequestWrapper(httpRequest, decryptedData);
            
            log.debug("包装请求创建完成，参数数量: {}", wrappedRequest.getParameterMap().size());
            
            // 继续处理请求
            chain.doFilter(wrappedRequest, response);
            
        } catch (Exception e) {
            log.error("处理请求时发生错误: {}", e.getMessage(), e);
            chain.doFilter(request, response);
        }
    }
    
    @Override
    public void destroy() {
        log.info("ApiEncryptFilter 销毁");
    }
    
    /**
     * 解密后的请求包装器
     */
    private static class DecryptedRequestWrapper extends HttpServletRequestWrapper {
        
        private final String decryptedBody;
        private final Map<String, String[]> parameterMap;
        
        public DecryptedRequestWrapper(HttpServletRequest request, String decryptedBody) {
            super(request);
            this.decryptedBody = decryptedBody;
            this.parameterMap = new HashMap<>();
            
            log.debug("创建DecryptedRequestWrapper，解密数据: {}", decryptedBody);
            
            // 先获取URL查询参数（GET参数）
            String queryString = request.getQueryString();
            if (queryString != null && !queryString.trim().isEmpty()) {
                log.debug("解析URL查询参数: {}", queryString);
                parseFormParameters(queryString);
            }
            
            // 解析解密后的数据为参数（POST参数）
            parseParameters(decryptedBody);
            
            log.debug("最终参数Map大小: {}, 内容: {}", parameterMap.size(), parameterMap);
        }
        
        /**
         * 解析参数
         */
        private void parseParameters(String data) {
            if (data == null || data.trim().isEmpty()) {
                log.debug("解析参数：数据为空");
                return;
            }
            
            log.debug("开始解析参数，数据长度: {}, 内容: {}", data.length(), data);
            
            try {
                // 处理JSON格式
                if (data.trim().startsWith("{") && data.trim().endsWith("}")) {
                    log.debug("检测到JSON格式数据");
                    parseJsonParameters(data.trim());
                } else {
                    // 处理表单格式
                    log.debug("检测到表单格式数据");
                    parseFormParameters(data);
                }
                log.debug("参数解析完成，当前参数数量: {}", parameterMap.size());
            } catch (Exception e) {
                log.error("解析参数失败: {}", e.getMessage(), e);
            }
        }
        
        /**
         * 解析JSON格式参数
         */
        private void parseJsonParameters(String json) {
            log.debug("解析JSON参数: {}", json);
            
            // 简单的JSON解析，去掉大括号
            String content = json.substring(1, json.length() - 1);
            
            // 改进的JSON解析逻辑，正确处理包含逗号的值
            int start = 0;
            boolean inQuotes = false;
            
            for (int i = 0; i < content.length(); i++) {
                char c = content.charAt(i);
                
                if (c == '"') {
                    inQuotes = !inQuotes;
                } else if (c == ',' && !inQuotes) {
                    // 找到键值对分隔符
                    String pair = content.substring(start, i).trim();
                    parseSingleJsonPair(pair);
                    start = i + 1;
                }
            }
            
            // 处理最后一个键值对
            if (start < content.length()) {
                String pair = content.substring(start).trim();
                parseSingleJsonPair(pair);
            }
            
            log.debug("JSON解析完成，参数数量: {}", parameterMap.size());
        }
        
        private void parseSingleJsonPair(String pair) {
            if (pair.trim().isEmpty()) {
                return;
            }
            
            int colonIndex = pair.indexOf(':');
            if (colonIndex > 0) {
                String key = pair.substring(0, colonIndex).trim().replaceAll("\"", "");
                String value = pair.substring(colonIndex + 1).trim();
                
                // 处理字符串值的双引号
                if (value.startsWith("\"") && value.endsWith("\"")) {
                    value = value.substring(1, value.length() - 1);
                }
                
                log.debug("解析JSON键值对: {} = {}", key, value);
                
                if (!key.isEmpty()) {
                    parameterMap.put(key, new String[]{value});
                }
            }
        }
        
        /**
         * 解析表单格式参数
         */
        private void parseFormParameters(String data) {
            if (data == null || data.trim().isEmpty()) {
                return;
            }
            
            log.debug("解析表单参数: {}", data);
            
            // 先对整个数据进行一次URL解码，处理可能的双重编码
            String decodedData = data;
            try {
                decodedData = java.net.URLDecoder.decode(data, "UTF-8");
                log.debug("整体URL解码后: {}", decodedData);
            } catch (Exception e) {
                log.debug("整体URL解码失败，使用原始数据: {}", e.getMessage());
                decodedData = data;
            }
            
            String[] pairs = decodedData.split("&");
            for (String pair : pairs) {
                if (pair.trim().isEmpty()) {
                    continue;
                }
                
                String[] keyValue = pair.split("=", 2);
                if (keyValue.length >= 1) {
                    String key = keyValue[0].trim();
                    String value = keyValue.length == 2 ? keyValue[1].trim() : "";
                    
                    // 对键值再次进行URL解码，处理特殊字符
                    try {
                        key = java.net.URLDecoder.decode(key, "UTF-8");
                        if (!value.isEmpty()) {
                            value = java.net.URLDecoder.decode(value, "UTF-8");
                        }
                        log.debug("解析参数: {} = {}", key, value);
                    } catch (Exception e) {
                        log.warn("参数URL解码失败 [{}={}]: {}", key, value, e.getMessage());
                        // 解码失败时使用原始值
                    }
                    
                    if (!key.isEmpty()) {
                        // 处理同名参数，支持数组
                        String[] existingValues = parameterMap.get(key);
                        if (existingValues != null) {
                            // 合并同名参数
                            String[] newValues = new String[existingValues.length + 1];
                            System.arraycopy(existingValues, 0, newValues, 0, existingValues.length);
                            newValues[existingValues.length] = value;
                            parameterMap.put(key, newValues);
                        } else {
                            parameterMap.put(key, new String[]{value});
                        }
                    }
                }
            }
        }
        
        @Override
        public String getParameter(String name) {
            log.debug("getParameter被调用，参数名: {}", name);
            String[] values = parameterMap.get(name);
            String result = values != null && values.length > 0 ? values[0] : null;
            log.debug("getParameter结果: {} = {}", name, result);
            return result;
        }
        
        @Override
        public String[] getParameterValues(String name) {
            log.debug("getParameterValues被调用，参数名: {}", name);
            String[] result = parameterMap.get(name);
            log.debug("getParameterValues结果: {} = {}", name, Arrays.toString(result));
            return result;
        }
        
        @Override
        public Map<String, String[]> getParameterMap() {
            log.debug("getParameterMap被调用，返回参数数量: {}", parameterMap.size());
            // 直接返回我们解析的参数Map，不依赖super.getParameterMap()
            return new HashMap<>(parameterMap);
        }
        
        @Override
        public Enumeration<String> getParameterNames() {
            log.debug("getParameterNames被调用，返回参数名列表: {}", parameterMap.keySet());
            return Collections.enumeration(parameterMap.keySet());
        }
        
        @Override
        public String getContentType() {
            return "application/x-www-form-urlencoded";
        }
        
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
                decryptedBody.getBytes(StandardCharsets.UTF_8));
            
            return new ServletInputStream() {
                @Override
                public boolean isFinished() {
                    return byteArrayInputStream.available() == 0;
                }
                
                @Override
                public boolean isReady() {
                    return true;
                }
                
                @Override
                public void setReadListener(ReadListener readListener) {
                    // 不需要实现
                }
                
                @Override
                public int read() throws IOException {
                    return byteArrayInputStream.read();
                }
            };
        }
        
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
        }
    }
}